#include "Tools.h"
#include "Exception.h"

typedef mMeshEntityContainer::iter iterall;

enum RestrictionOperator {PURE_INJECTION, HALF_WEIGHTING, FULL_WEIGHTING};
enum InterpolationOperator {LINEAR, QUADRATIC, ADAPTATIVE, CONSERVATIVE};

/*! \class DataTransfer
 *  \brief This class contains all the variables and procedures related to the multigrid data transfer.
 */

class DataTransfer {
  
  public:
  
  /*! \brief: Constructor is called when the class is created. It creates the GeomCoeff, pLinear, NodeValues and Octree.
   * \param mesh1 GeomCoeff, NodeValues and Octree is constructed based on this mesh.
   * \param mesh2 pLinear is constructed based on this mesh.
   */
  DataTransfer(pMesh mesh1, pMesh mesh2);
  ~DataTransfer();
  
  /*! \brief: Realize the data transfer from the fine to the coarse mesh.
   * \param fine fine mesh
   * \param coarse coarse mesh.
   */
  
  void fine_to_coarse(pMesh fine, pMesh coarse);

    /*! \brief: Realize the data transfer from the coarse to the fine mesh.
   * \param coarse coarse mesh.
   * \param fine fine mesh
   */
  
  void coarse_to_fine(pMesh coarse, pMesh fine);
  
 /*! \brief: Sets the restriction method.
   * \param RestrictionOperator type of restriction mesthod (pure injection, half weighting or full weighting).
   */
  
  void setRestrictionOperator(RestrictionOperator rOp){ restrictionOp = rOp; }
  
 
   /*! \brief: Sets the interpolation method.
   * \param InterpolationOperator type of interpolation mesthod (linear, quadratic, adaptative or conservative).
   */
    
  void setInterpolationOperator(InterpolationOperator iOp){ interpolationOp = iOp; }
 
  /*! \brief: Returns the restriction method.
   */
  RestrictionOperator getRestrictionOperator() const{ return restrictionOp; }


  const double* getInterpolatedData() const { return pInterpolatedVals; }
  
   /*! \brief: Allocate the nodes value in a array.
   * \param id It is the node id from the mesh from where the data is being transfered.
   * \param value Value to be stored.
   */
  void setNodeValue(int id, double value) {pNodeValue[id] = value;}
  
   /*! \brief: Get the desired node value.
   * \param id It is the node id from the mesh from where the data is being transfered.
   */
  
  double getNodeValue(int id) {return pNodeValue[id];}
  
    /*! \brief: Get the linear interpolation result.
   * \param id It is the node id from the mesh to where the data is being transfered.
   */
  
  double getInterpolatedValues(int id) {return pInterpolatedVals[id];}

  // debuggin functions
  void showScalarField() const;

  const double* getScalarField_baseMesh() const { return pNodeValue; }
    
  bool isHalfWeighting();
  
  private:

//variables

    RestrictionOperator restrictionOp; 		//Restriction method
    InterpolationOperator interpolationOp; 	//Interpolation method
    double **pGeomCoeff;			//Geometric Coefficients matrix 			
    double *pNodeValue;				//Node Values array
    Octree *theOctree;				//Octree
    double *pInterpolatedVals;		//Interpolation result
    double **pGrad;				//Gradients

//functions

    double Assembly_Mat_Vec(pMesh theMesh,Mat M, Vec Fx, Vec Fy);
    
    double calculate_SecondOrderDerivatives(pMesh theMesh);
    
    double store_FirstOrderDerivatives(pMesh theMesh,Vec gradx, Vec grady);
    

    //COMENTARIOS A LA DOXYGEN!!
    void addNeighborsWeighting(pMesh mesh);
	        
    /*! \brief: Returns the interpolation method.
   */
    
  InterpolationOperator getInterpolationOperator() const{ return interpolationOp; } 

  /*! \brief: Allocate the calculated geometric coefficients in a matrix.
   * \param row It is the node id from the mesh to where the data is being transfered.
   * \param column It can be 3 or 4, dependind on the dimension. It is related to the nodes of the element found with the Octree.
   * \param value Value to be stored.
   */
  
  void setGeomCoeff(int row, int column, double value) {pGeomCoeff[row][column]= value;}

  /*! \brief: Get the calculated geometric coefficient.
   * \param row It is the node id from the mesh to where the data is being transfered.
   * \param column It can be 3 or 4, dependind on the dimension. It is related to the nodes of the element found with the Octree.
   */
  
  double getGeomCoeff(int row, int column) {return pGeomCoeff[row][column];}

    /*! \brief: Store the linear interpolation in a array.
   * \param id It is the node id from the mesh to where the data is being transfered.
   */
  
  void setInterpolatedValues(int id, double value) {pInterpolatedVals[id]= value;}
  
  /*! \brief: Calculate the Geometric Coefficients (Area or Volume coordinates).
   * \param mesh mesh from where the data is being transfered.
   * \param dim mesh dimension.
   */
    
  void calculate_GeomCoeff(pMesh mesh, int dim);

  /*! \brief: Calculate linear interpolation.
   * \param mesh mesh from where the data is being transfered.
   * \param dim mesh dimension.
   */
  
  void calculate_LinearInterpolation(pMesh mesh,pMesh mesh1, int dim);
  
  /*! \brief: Calculate quadratic interpolation.
   * \param mesh mesh from where the data is being transfered.
   * \param dim mesh dimension.
   */
  
  double calculate_QuadraticInterpolation(pMesh theMesh, int dim);
  
  /*! \brief: Calculate gradients (used for quadratic interpolation).
   * \param theMesh mesh from where the data is being transfered.
   */
  
  double calculate_Gradients(pMesh theMesh);

  /*! \brief: Allocate the gradients in a array.
   * \param id It is the node id from the mesh from where the data is being transfered.
   * \param value Value to be stored.
   */
  
  void setGrad(int id, int column,double value) {pGrad[id][column]= value;}

  /*! \brief: Get the desired node gradient.
   * \param id It is the node id from the mesh from where the data is being transfered.
   */

  double getGrad(int id, int column) {return pGrad[id][column];}
	    
};
